/**
 * Library to generate an HTML report
 */

var Canvas = function (inputObj) {
	this._setCSSClasses();
	this.componentType = "Canvas";
	this.inputObj = inputObj;
	this.element = this._setComponent(inputObj.parentid);
	if(inputObj.properties) {
		this.properties = {
			decimalSeparator : inputObj.properties.decimalSeparator || ".",
			showPageNumber : inputObj.properties.showPageNumber,
			pageNumberStyles : inputObj.properties.pageNumberStyles
		}
	}
	this.pages = [];
}

Canvas.prototype = (function () {
	
	/**
	 * Checks if the iReport classes have been added. If not added, adds them to the document.
	 */
	var _setCSSClasses = function () {
		var head = document.getElementsByTagName("head")[0];
		var styleElmt = document.getElementById("ireportStyles") || Utility.createHTMLElement("style", {
			type : "text/css",
			id : "ireportStyles"
		}, head);
	    var styletext = '.heading {font-size: 44px;}\
						.fieldlabel {float : left;width : 156px;height : 25px;}\
						.field {float : left;width : 100px;height : 25px;}\
						.canvas {background-color: #EBEBEB;overflow: auto;}\
						.page {background-color: #FFFFFF;margin: 10px;position : relative;}\
						.page > div {position: absolute;font-family: Helvetica !important;}\
						.pagebreak {width:712px;height:1px;background-color: #000000;left: 43px;}\
						.fieldbox {clear: both;}\
						.fieldbox div {font-size:17px; font-family: Helvetica !important; text-align: left;}\
						.subheading {font-size: 26px;text-decoration: underline;}\
						.gridbox {width:712px;table-layout : fixed; word-break : break-all;}\
						.gridbox th {padding : 0px; font-family : Helvetica !important; vertical-align : middle;text-align : center;border-top : 1px solid #000000;border-bottom : 1px solid #000000;font-size: 15px;}\
						.gridbox td {text-align : left;font-family : Helvetica !important; font-size : 15px; padding : 0px;}';
	    if(styleElmt.styleSheet)    //for IE
	        styleElmt.styleSheet.cssText=styletext;
	    else                        //for browsers
	        styleElmt.innerHTML=styletext;
	}
	
	/**
	 * To set the HTML for the canvas.
	 * @param {Object} parentid - The parent element to which the canvas is to be attached
	 */
	function _setComponent(parentid) {
		var canvas = Utility.createHTMLElement("div", {
			"class" : "canvas"
		}, this.inputObj.parentid);
		this.element = canvas;
		var styles = this.inputObj.styles;
		if(styles) {
			styles.width = !styles.width ? styles.width : "836px"
			styles.height = !styles.height ? styles.height : "702px"
		} else {
			styles = {
				width : "836px",
				height : "702px"
			}
		}
		this.setStyles(styles);		
		return canvas;
	}

	/**
	 * To set styles for the canvas
	 * @param {Object} styleObj
	 */
	var setStyles = function (styleObj) {
		Utility.addStyles(this.element, styleObj);
	}
	
	/**
	 * To add a page to a canvas
	 * @param {Object} Page object
	 * @param {boolean} ignorePageNumber - if true, page numbers are not updated after adding the page
	 */
	var addPage = function (page, ignorePageNumber) {
		this.element.appendChild(page.element);
		this.pages.push(page);
		page.canvas = this;
		page.setPosition();
		Utility.addStyles(page.pageNumberElmt, page.canvas.properties.pageNumberStyles);
		!ignorePageNumber && setPageNumbers(this.pages, this.properties.showPageNumber);
	}
	
	/**
	 * To remove a page at the specified page number
	 * @param {Object} pageNumber
	 */
	var removePage = function (pageNumber) {
		var page = this.pages[pageNumber - 1];
		this.element.removeChild(page.element);
		page = null;
		this.pages.splice(pageNumber - 1, 1);
		setPageNumbers(this.pages);
	}
	
	/**
	 * Returns object with properties
	 * horizontal - true if the horizontal scroll is visible
	 * vertical - true if the vertical scroll is visible
	 */
	var isScrollVisible = function () {
		var returnObj = {};
		var page = this.pages[0];
		var pageHeight = Utility.removePx(page.element.style.height);
		var margin = Utility.removePx(page.element.style.margin);
		var canvasHeight = Utility.removePx(this.element.style.height);
		if((pageHeight + margin) > canvasHeight) {
			returnObj.vertical = true;
		}
		return returnObj;
	}
	
	/**
	 * To add multiple pages to a canvas
	 * @param {Object} Array of Page objects
	 */
	var addPages = function (pages) {
		for(var i = 0; i < pages.length; i++) {
			this.addPage(pages[i], false);
		}
		setPageNumbers(this.pages);
	}
	
	/**
	 * Set the page numbers
	 * @param {Object} pages
	 */
	var setPageNumbers = function (pages, showPageNumber) {
		var pageNumberStr = "";
		for(var i = 0; i < pages.length; i++) {
			if(showPageNumber) {
				pageNumberStr = (i+1) + " of " + pages.length;
			}
			pages[i].setPageNumber(pageNumberStr, i+1);			
		}
	}
	
	/**
	 * Returns all the components from all the pages including and after the mentioned page number
	 * @param {Object} pageNumber
	 */
	var getComponentsFromPage = function (pageNumber) {
		var componentsArray = [];
		for(var i = pageNumber - 1; i < this.pages.length; i++) {
			componentsArray.push(this.pages[i].getComponentsFromPosition(0));
		}
		return componentsArray;
	}
	
	return {
		_setCSSClasses : _setCSSClasses,
		_setComponent : _setComponent,
		addPage : addPage,
		addPages : addPages,
		setStyles : setStyles,
		isScrollVisible : isScrollVisible,
		constructor : Canvas,
		removePage : removePage,
		getComponentsFromPage : getComponentsFromPage
	}
})();

/**
 * Utility functions
 */
var Utility = (function () {
	/**
	 * To create an HTML element
	 * type - type of the element to be created
	 * attributes - object containing the list of attributes
	 * parent - parent element id or the parent element itself to which the created element is to be attached
	 */	
	var createHTMLElement = function (type, attributes, parent) {
		var element = document.createElement(type);
		if(attributes) {
			for(var i in attributes) {
				element.setAttribute(i, attributes[i]);
			}
		}
		if(parent) {
			parent = parent.constructor == String ? document.getElementById(parent) : parent;
			parent.appendChild(element);
		}
		return element;
	}	
	
	/**
	 * Appends "px" to the end of a string if it is not already added.
	 * @param {Object} str
	 */
	var addPx = function (str) {
		return (/px$/.test(str)) ? str : (str + "px");			
	}	
	
	/**
	 * To remove the px from the end of a string and return it as a number. In case after removing px  
	 * from str, a valid number string is not obtained 0 is returned
	 * @param {Object} str
	 */
	var removePx = function (str) {
		return Number(str.replace("px","")) || 0;	
	}
	
	/**
	 * Add styles to an element
	 * @param {Object} element
	 * @param {Object} styles - styles object
	 * @param {Object} storeObj - the styles applied to the element are also added as parameters in this object and
	 * returned. This is useful for editing the inputObj when the user sets some styles
	 */
	var addStyles = function (element, styles, storeObj) {
		for(var i in styles) {
			element.style[i] = styles[i];
			if(storeObj) {
				storeObj[i] = styles[i];			
			}
		}		
	}
	
	/**
	 * To get the computed style of the element as a number
	 * @param {Object} element
	 * @param {Object} property
	 */
	var getComputedStyle = function (element, property) {
		return this.removePx(window.getComputedStyle(element, null).getPropertyValue(property));
	}
	
	/**
	 * To convert a value into a suitable display value based on the properties of the canvas
	 * @param {Object} type - Number, String
	 * @param {Object} value - value to be transformed
	 * @param {Object} properties - properties object of the canvas
	 */
	var getValueByType = function (type, value, properties) {
		if(type == "Number") {
			return value.toString().replace(".", properties.decimalSeparator);
		}
		return value;
	}
	
	/**
	 * To create a copy of an object
	 * @param {Object} srcObject
	 * @param {Object} targetObject
	 */
	var copyObject = function(srcObject, targetObject) {
		targetObject = targetObject || (srcObject instanceof Array ? [] : {});
		for(var i in srcObject) {
			if (typeof(srcObject[i]) == "object") {
				targetObject[i] = copyObject(srcObject[i]);
			} else {
				targetObject[i] = srcObject[i];
			}
		}
		return targetObject;
	}
	
	/**
	 * To get the top + height of an element. The pixel position where the element ends w.r.t top of a page
	 * @param {Object} element
	 */
	var getElementEndPosition = function (element) {
		var computedTop = Utility.getComputedStyle(element, "top");
		var computedHeight = Utility.getComputedStyle(element, "height");
		return computedHeight + computedTop;		
	}
	
	return {
		createHTMLElement : createHTMLElement,
		addPx : addPx,
		addStyles : addStyles,
		removePx : removePx,
		getComputedStyle : getComputedStyle,
		getValueByType : getValueByType,
		copyObject : copyObject,
		getElementEndPosition : getElementEndPosition
	}
})();